package com.gulimall.product.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.gulimall.product.service.AttrService;
import com.gulimall.product.service.CategoryBrandRelationService;
import com.gulimall.product.vo.categoryvo.Category2Vo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gulimall.common.utils.PageUtils;
import com.gulimall.common.utils.Query;

import com.gulimall.product.dao.CategoryDao;
import com.gulimall.product.entity.CategoryEntity;
import com.gulimall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    private AttrService attrService;
    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> treeList() {
        //1、查所有的分类
        List<CategoryEntity> allCategory = baseMapper.selectList(null);

        //2、组装父子的树形结构
        //2.1、找到所有的一级分裂
        List<CategoryEntity> levelOneMenu = allCategory.stream().filter((item) ->
                item.getParentCid().equals(0L)
        ).map((childrens) -> {
            childrens.setChildren(getChildrens(allCategory, childrens));
            return childrens;
        }).sorted((menu1, menu2) -> {
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());
        return levelOneMenu;
    }

    /**
     * 判断要删除的分类是否在其他的地方有引用
     * 逻辑删除的方式
     *
     * @param asList
     */
    @Override
    public void removeCateGoryByIds(List<Long> asList) {
        // TODO 检查当前删除的分类，是否在其他的地方有引用
        baseMapper.deleteBatchIds(asList);
    }

    /**
     * 级联更新所有关联的数据
     *
     * @param category
     */
    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
        //先更新自身表中的数据
        this.updateById(category);

        if (StringUtils.isNotEmpty(category.getName())) {
            //更新级联
            categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());

            //更新attr表
            attrService.updateCategory(category.getCatId(), category.getName());
        }
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        ArrayList<Long> catelogPath = new ArrayList<>();

        //根据查询到的属性分组查询分类的父子孙id
        getCatelogPath(catelogPath, catelogId);
        //逆置 将catelogPath逆序转换 {225,25,2} --> {2,25,225}
        Collections.reverse(catelogPath);
        //转换为Long[] 类型 --> [2,25,225]
        return catelogPath.toArray(new Long[catelogPath.size()]);
    }

    /**
     * 查找所有的一级分类
     *
     * @return
     */
    @Override
    public List<CategoryEntity> getLevelOneCategorys() {
        return this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    }

    /**
     * 整合redis缓存的获取三级分类接口
     *
     * @return
     */
    @Override
    public Map<Long, List<Category2Vo>> getCatalogJson() {
        //先从redis中获取键为catalogJson的缓存数据
        String catalogJson = redisTemplate.opsForValue().get("catalogJson");
        //判断缓存中是否存在该键的数据
        if (StringUtils.isEmpty(catalogJson)) {
            //如果是空的说明redis中没有相关数据，根据写好的获取三级分类的接口去获取相关的数据，并存入到redis中
            Map<Long, List<Category2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
            //将获取到的数据转为JSON类型，JSON是跨语言，跨平台全部兼容的类型
            String jsonString = JSONObject.toJSONString(catalogJsonFromDb);
            //将转换好的数据存入到redis中
            redisTemplate.opsForValue().set("catalogJson", jsonString);

            //将本次查询到的数据直接返会
            return catalogJsonFromDb;
        }
        //如果从redis中成功获取到相关数据直接转换为所需类型返回
        //获取到的类型为JSON类型，需要将JSON转换为所需的类型
        return JSONObject.parseObject(catalogJson, new TypeReference<Map<Long, List<Category2Vo>>>() {
        });
    }

    /**
     * 查询二级三级分类
     * 方法1（查询多次，多次与数据库交互）、
     * 先查询一级分类
     * 再查询二级分类
     * 再查询三级分类
     * 方法2（仅查询一次数据库）、
     * 查询所有分类
     * 依次过滤收集一级二级三级分类
     * 方法3（将方法2中的冗余方法提取为公共方法进行调用）
     */
    public Map<Long, List<Category2Vo>> getCatalogJsonFromDb() {
        /*============================== 方法1 （本机测试吞吐量7.5±） ****************************/
        /*//1、查所有的一级分类
        List<CategoryEntity> levelOneCategorys = getLevelOneCategorys();
        //封装数据
        Map<Long, List<Category2Vo>> collect = levelOneCategorys.stream().collect(Collectors.toMap(k -> k.getCatId(), v -> {
            //遍历每个一级 分类 查这个一级分类下的所有二级分类 （查找所有父级id为当前cid的记录）
            List<CategoryEntity> level2Categorys = baseMapper.selectList(
                    new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));

            //用于封装二级分类
            List<Category2Vo> category2VoList = null;

            //判断是否不为空
            if (!level2Categorys.isEmpty()) {
                //如果不为空
                category2VoList = level2Categorys.stream().map(level2 -> {
                    Category2Vo category2Vo = new Category2Vo(level2.getParentCid(), level2.getCatId(), level2.getName(), null);

                    //封装三级分类
                    List<CategoryEntity> level3Categorys = baseMapper.selectList(
                            new QueryWrapper<CategoryEntity>().eq("parent_cid", level2.getCatId()));
                    if (!level3Categorys.isEmpty()) {
                        List<Category2Vo.category3Vo> category3VoList = level3Categorys.stream().map(level3 -> {
                            Category2Vo.category3Vo category3Vo = new Category2Vo.category3Vo(level3.getParentCid(), level3.getCatId(), level3.getName());
                            return category3Vo;
                        }).collect(Collectors.toList());

                        category2Vo.setCatalog3List(category3VoList);
                    }

                    return category2Vo;
                }).collect(Collectors.toList());
            }
            return category2VoList;
        }));*/

        /*============================== 方法2 （本机测试吞吐量99±） ****************************/
        /*//1、先查询所有的分类
        List<CategoryEntity> allCategories = baseMapper.selectList(null);
        //收集要返回的数据
        Map<Long, List<Category2Vo>> collect = allCategories.stream().filter(category -> {
            //对一级分类进行过滤收集
            return category.getParentCid().equals(0L);
        }).collect(Collectors.toMap(CategoryEntity::getCatId, v -> {
            //收集所有的二级分类
            List<Category2Vo> category2VoList = allCategories.stream().filter(category -> {
                return category.getParentCid().equals(v.getCatId());
            }).map(level2Category -> {
                Category2Vo category2Vo = new Category2Vo();
                category2Vo.setCatalog1Id(level2Category.getParentCid());
                category2Vo.setId(level2Category.getCatId());
                category2Vo.setName(level2Category.getName());

                //收集三级分类
                List<Category2Vo.category3Vo> category3VoList = allCategories.stream().filter(category -> {
                    return category.getParentCid().equals(level2Category.getCatId());
                }).map(level3Category -> {
                    Category2Vo.category3Vo category3Vo = new Category2Vo.category3Vo();
                    category3Vo.setCatalog2Id(level3Category.getParentCid());
                    category3Vo.setId(level3Category.getCatId());
                    category3Vo.setName(level3Category.getName());
                    return category3Vo;
                }).collect(Collectors.toList());
                category2Vo.setCatalog3List(category3VoList);

                return category2Vo;
            }).collect(Collectors.toList());

            return category2VoList;
        }));*/

        /*============================== 方法3 （本机测试吞吐量105±） ****************************/
        //1、先查询所有的分类
        List<CategoryEntity> allCategories = baseMapper.selectList(null);
        //调用方法收集一级分类
        Map<Long, List<Category2Vo>> collect = getCollect(allCategories, 0L).stream().collect(Collectors.toMap(k -> k.getCatId(), v -> {
            //获取当前分类的二级分类
            List<CategoryEntity> level2Categories = getCollect(allCategories, v.getCatId());
            //如果二级分类不为空进行查询三级分类并收集数据
            List<Category2Vo> category2VoList = new ArrayList<>();
            if (!level2Categories.isEmpty()) {
                category2VoList = level2Categories.stream().map(level2Category -> {
                    Category2Vo category2Vo = new Category2Vo(level2Category.getParentCid(), level2Category.getCatId(), level2Category.getName(), null);
                    //查找当前分类的三级分类
                    List<CategoryEntity> level3Categories = getCollect(allCategories, level2Category.getCatId());
                    if (!level3Categories.isEmpty()) {
                        List<Category2Vo.category3Vo> category3VoList = level3Categories.stream().map(level3Category -> {
                            return new Category2Vo.category3Vo(level3Category.getParentCid(), level3Category.getCatId(), level3Category.getName());
                        }).collect(Collectors.toList());
                        //将收集到的数据存入2Vo中
                        category2Vo.setCatalog3List(category3VoList);
                    }
                    return category2Vo;
                }).collect(Collectors.toList());
            }

            return category2VoList;
        }));

        return collect;
    }

    /**
     * 根据上面接口提取出来的公共方法
     *
     * @param allCategories
     * @param parentId
     * @return
     */
    private List<CategoryEntity> getCollect(List<CategoryEntity> allCategories, Long parentId) {
        return allCategories.stream().filter(category ->
                category.getParentCid().equals(parentId)
        ).collect(Collectors.toList());
    }

    //用于递归查询父子孙节点
    private void getCatelogPath(List<Long> catelogPath, Long catelogId) {
        //先将要查询的节点添加到集合中
        catelogPath.add(catelogId);

        //根据该集合查询详细节点信息，判断父节点id是否=0，等于0说明不用再进行查询
        CategoryEntity categoryEntity = baseMapper.selectById(catelogId);
        if (!categoryEntity.getParentCid().equals(0L)) {
            getCatelogPath(catelogPath, categoryEntity.getParentCid());
        }

    }

    //递归算法，从所有的分类中查找子分类
    public List<CategoryEntity> getChildrens(List<CategoryEntity> allCategory, CategoryEntity category) {
        List<CategoryEntity> childrens = allCategory.stream().filter((item) -> {
            return item.getParentCid().equals(category.getCatId());
        }).map((children) -> {
            //找到子菜单
            children.setChildren(getChildrens(allCategory, children));
            return children;
        }).sorted((menu1, menu2) -> {
            //菜单的排序
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());
        return childrens;
    }
}